package com.tt5.services.starter

import com.tt5.services.error.ApiException
import com.tt5.services.handlers.AppRouter
import com.tt5.services.vertx.coroutineHandler
import com.tt5.services.vertx.handlers.LogHandler
import com.tt5.services.vertx.handlers.failure.FailureHandler
import io.netty.handler.codec.http.HttpResponseStatus
import io.vertx.config.ConfigRetriever
import io.vertx.core.http.HttpMethod
import io.vertx.core.http.HttpServerOptions
import io.vertx.core.impl.logging.Logger
import io.vertx.core.impl.logging.LoggerFactory
import io.vertx.ext.auth.JWTOptions
import io.vertx.ext.web.AllowForwardHeaders
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.StaticHandler
import io.vertx.kotlin.config.configRetrieverOptionsOf
import io.vertx.kotlin.config.configStoreOptionsOf
import io.vertx.kotlin.core.json.get
import io.vertx.kotlin.core.json.jsonObjectOf
import io.vertx.kotlin.coroutines.CoroutineVerticle
import io.vertx.kotlin.coroutines.coAwait

class HttpServerVerticle : CoroutineVerticle() {
  val logger: Logger = LoggerFactory.getLogger(javaClass.name)

  override suspend fun start() {
    val config = ConfigRetriever.create(
      vertx, configRetrieverOptionsOf(
        stores = listOf(
          configStoreOptionsOf(
            type = "file",
            config = jsonObjectOf(Pair("path", "conf/serve.json"))
          )
        )
      )
    ).config.coAwait()
    val configJwt = ConfigRetriever.create(
      vertx, configRetrieverOptionsOf(
        stores = listOf(
          configStoreOptionsOf(
            type = "file",
            config = jsonObjectOf(Pair("path", "conf/jwt.json"))
          )
        )
      )
    ).config.coAwait()
    AppRouter.jwtAccessOpt = JWTOptions(configJwt)
    AppRouter.jwtRefreshOpt = JWTOptions(configJwt.mergeIn(configJwt["refresh"]))

    val router = Router.router(vertx).allowForward(AllowForwardHeaders.FORWARD)

    router.route("/static").method(HttpMethod.GET).method(HttpMethod.HEAD).handler(
      StaticHandler.create(config.getString("static_root")).setDefaultContentEncoding("UTF-8")
    )
    router.route().coroutineHandler(LogHandler::log)

    AppRouter.create(vertx, router)

    router.route().handler { ctx ->
      throw ApiException(
        code = HttpResponseStatus.NOT_FOUND.code(),
        content = "来到了知识的荒原",
        errorMessage = "${ctx.request().uri()} not found"
      )
    }.failureHandler(FailureHandler())

    val httpServerStarter = vertx
      .createHttpServer(HttpServerOptions(config))
      .requestHandler(router)
      .listen()
    val httpServer = httpServerStarter.coAwait()
    if (httpServerStarter.succeeded()) {
      logger.info("is now running within ${httpServer.actualPort()}!")
    } else {
      logger.error("start failed: ${httpServerStarter.cause().stackTraceToString()}")
    }
  }

  override suspend fun stop() {
    logger.info("stopped")
    super.stop()
  }
}
